package com.chapter.sample.answer.multiplex;

import lombok.Getter;

import java.util.List;
import java.util.concurrent.*;

/**
 * 正确关闭线程池姿势--优雅停机
 */
public class ThreadPoolGracefulShutdownDemo {
    public static void main(String[] args) {
        //shutdown_Test();
        //shutdownNow_Test();
        shutdown_awaitTermination_Test();
        //shutdownNow_awaitTermination_Test();
    }

    private static void shutdownNow_awaitTermination_Test() {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //提交10个任务，在第5个任务提交完，准备提交第6个的时候shutdown
        for (int i = 1; i <= 10; i++) {
            System.out.println("第：" + i + "次提交");
            try {
                threadPool.execute(new Task(i));
            } catch (RejectedExecutionException e) {
                System.out.println("rejected，task-" + i);
            }
            //i等于5的时候shutdown，意味着从第6次开始就不能提交新任务了
            if (i == 5) {
                List<Runnable> tasks = threadPool.shutdownNow();
                for (Runnable task : tasks) {
                    if (task instanceof Task) {
                        System.out.println("waiting task：" + ((Task) task).getName());
                    }
                }
            }
        }

        try {
            /**
             * 任务没执行完，且没有到指定时间，时不会执行下面两行打印代码的
             *
             * 现在把等待时间设置成4秒，达到设置时间后，就不在阻塞当前线程了
             * 直接打印了下面两行代码，并且返回了false，说明线程池没有停止
             *
             * 有时候我们需要主线程等所有子线程执行完毕后再运行，再所有任务提交后，
             * 调用shutdown触发awaitTermination，阻塞主线程，当所有子线程都执行完毕后，解除阻塞
             */
            boolean isStop = threadPool.awaitTermination(4, TimeUnit.SECONDS);
            System.out.println("is pool isStop：" + isStop);
            System.out.println(Thread.currentThread().getName() + " 执行：2222222222222222222222222222222222222");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("--------------------------");
        System.out.println(Thread.currentThread().getName() + "\t" + "mission is over");
        System.out.println();
    }

    private static void shutdown_awaitTermination_Test() {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //提交10个任务，在第5个任务提交完，准备提交第6个的时候shutdown
        for (int i = 1; i <= 10; i++) {
            System.out.println("第：" + i + "次提交");
            try {
                threadPool.execute(new Task(i));
            } catch (RejectedExecutionException e) {
                System.out.println("rejected，task-" + i);
            }
            //i等于5的时候shutdown，意味着从第6次开始就不能提交新任务了
            if (i == 5) {
                threadPool.shutdown();
                System.out.println("i等于5的时候shutdown,意味着从第6次开始就不能提交新任务");
                System.out.println();
            }
        }

        try {
            /**
             * 任务没执行完，且没有到指定时间，时不会执行下面两行打印代码的
             *
             * 现在把等待时间设置成4秒，达到设置时间后，就不在阻塞当前线程了
             * 直接打印了下面两行代码，并且返回了false，说明线程池没有停止
             *
             * 有时候我们需要主线程等所有子线程执行完毕后再运行，再所有任务提交后，
             * 调用shutdown触发awaitTermination，阻塞主线程，当所有子线程都执行完毕后，解除阻塞
             */
            boolean isStop = threadPool.awaitTermination(4, TimeUnit.SECONDS);
            System.out.println("is pool isStop：" + isStop);
            System.out.println("执行：111111111111111111111111111111");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("--------------------------");
        System.out.println(Thread.currentThread().getName() + "\t" + "mission is over");
        System.out.println();
    }

    /**
     * 调用shutdownNow后，第一个任务正在睡眠的时候，触发了interrupt中断
     * 之后等待的任务2-5被从队列中清除并返回，之后的任务6-10被拒绝
     * but
     * shutdownNow 方法将线程池状态设置为STOP，试图让线程池立即停止
     * 但不一定能保证立即停止，要等所有正在执行的任务，（不能被interrupt中断的任务）执行完才能停止
     */
    private static void shutdownNow_Test() {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();

        for (int i = 1; i <= 10; i++) {
            try {
                threadPool.execute(new Task(i));
            } catch (RejectedExecutionException e) {
                System.out.println("rejected，task-" + i);
            }
            if (i == 5) {
                List<Runnable> tasks = threadPool.shutdownNow();
                for (Runnable task : tasks) {
                    if (task instanceof Task) {
                        System.out.println("waiting task" + ((Task) task).getName());
                    }
                }
            }
        }
    }


    //参考官网，最后的终结，优雅关停,but优点费事

    /**
     * 第6个任务开始以及之后的任务都被拒绝了，1-5号任务正常执行
     * 所以shutdown方法将线程池状态设置为 shutdown,线程池并不会立即停止
     * 要等正在执行和队列里等待的任务执行完才会停止
     */
    public static void shutdown_Test() {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //提交10个任务，在第5个任务提交完,准备提交第6个的时候执行shutdown
        for (int i = 1; i <= 10; i++) {
            System.out.println("第：" + i + "次提交");
            //此处故意不加try....catch块,方便演示
            threadPool.execute(new Task(i));
            //i等于5的时候shutdown,意味着从第6次开始不能提交新的任务
            if (i == 5) {
                threadPool.shutdown();
            }
        }
        System.out.println("xxxxxxxxxxxxxxxxxxxxx");
    }

    /**
     * 停机分2个阶段,
     * 1、shutdown一后就拒绝了新进入的任务，
     * 启动一个有序的关闭程序，再这个过程中，之前已经提交的任务将被执行,但不会接受新任务.如果系统已经关闭,那么再次调用此方法将不回产生额外的效果
     * 不会再接受新任务的提交
     * 再shutdown调用之前的任务会被执行下去,待执行的任务和正在执行的任务都不会取消,将继续执行
     * 如果已经shutdown了,再调用不会有其他影响
     * 2、然后再shutdownNow，然后去取消正在执行的任务
     *
     * @param threadPool
     */
    public void finalOK_shutdownAndAwaitTermination(ExecutorService threadPool) {
        if (threadPool != null && !threadPool.isShutdown()) {
            threadPool.shutdown();
            try {

                if (!threadPool.awaitTermination(120, TimeUnit.SECONDS)) {
                    threadPool.shutdownNow();
                    if (!threadPool.awaitTermination(123, TimeUnit.SECONDS)) {
                        System.out.println("pool dit not terminate");
                    }
                }
            } catch (Exception e) {
                threadPool.shutdown();
                Thread.currentThread().interrupt();
            }
        }
    }


    static class Task implements Runnable {

        @Getter
        String name;

        public Task(int i) {
            name = "task-" + i;
        }

        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println("sleep completed，" + getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("interrupted，" + getName());
                return;
            }
            System.out.println(getName() + " finished");
            System.out.println();
        }
    }

}


